package appnode;

import javax.jdo.PersistenceManager;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import appnode.Node;
import appnode.SearchNode;
import appnode.Link;
import appnode.Interface;
import appnode.Ratings;
import javax.jdo.Query;
import java.lang.*;


public class FreqTools {
	
	public static List<String> listfreq(String name1,String name2) {
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<String> links = frequences(name1,name2);
		int[] linklist = new int[links.size()];
		for (int i=0;i<links.size();i++) {
			Key keylink = KeyFactory.stringToKey(links.get(i));
			Link link = pm.getObjectById(Link.class,keylink);
			linklist[i] = Integer.valueOf(link.getFreq()).intValue();
		}
		Arrays.sort(linklist);
		for (int j=0;j<linklist.length;j++) {
			links.set(j,Integer.toString(linklist[j]));
		}
		pm.close();
		return links;
		
	}
	
	
	public static List<String> frequences(String name1, String name2) {
		
		//initialize
		List<String> result = new ArrayList<String>();
		
		/*Key key1 = KeyFactory.stringToKey(keystr1);
		Key key2 = KeyFactory.stringToKey(keystr2);
		
		Node node1 = pm.getObjectById(Node.class,key1);
		Node node2 = pm.getObjectById(Node.class,key2);*/
		
		Node node1 = SearchNode.getByName(name1);
	 	Node node2 = SearchNode.getByName(name2);
		double[] p1 = new double[2];
		double[] p2 = new double[2];
		double[] prjp1 = new double[2];
		double[] prjp2 = new double[2];
		double[] p3 = new double[2];
		double[] p4 = new double[2];
		double[] prjp3 = new double[2];
		double[] prjp4 = new double[2];
		String sect1,sect2,sect3,sect4;
		double cosine,sine;
		
		p1[0] = Double.valueOf(node1.getLng()).doubleValue();
		p1[1] = Double.valueOf(node1.getLat()).doubleValue();
		p2[0] = Double.valueOf(node2.getLng()).doubleValue();
		p2[1] = Double.valueOf(node2.getLat()).doubleValue();
		
		
		prjp1[0]= projlng(p1[0]);
		prjp1[1]= projlat(p1[1]);
		prjp2[0]= projlng(p2[0]);
		prjp2[1]= projlat(p2[1]);
		
		//intersect
		double[] a = new double[2];
		double[] b = new double[2];
				
		// transform
		double x,y;
		double r = 1;
		
		// switch p1,p2
		if (prjp1[0]>prjp2[0]) {
			x = prjp1[0];
			y = prjp1[1];
			prjp1[0] = prjp2[0];
			prjp1[1] = prjp2[1];
			prjp2[0] = x;
			prjp2[1] = y;
		}
		
	
		//double rotate = (p1[1]-p2[1])/(p1[0]-p2[0]);
		//double u = Math.toRadians(rotate);
		//cosine = Math.cos(u)/r;
		//sine = Math.sin(u)/r;
		cosine = (prjp2[0]-prjp1[0])/dist(prjp1,prjp2);
		sine = (prjp2[1]-prjp1[1])/dist(prjp1,prjp2);
	
		
		// angle > 90
		if (prjp1[1]>prjp2[1]) {
			cosine = -cosine;
			sine = -sine;
		}
	
		//scan links
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Link.class);
		List<Link> results = (List<Link>) query.execute();
		for (Link link : results) {
			Key key1 = KeyFactory.stringToKey(link.getName1());
			Key key2 = KeyFactory.stringToKey(link.getName2());
			Interface if1 = pm.getObjectById(Interface.class,key1);
			Interface if2 = pm.getObjectById(Interface.class,key2);
			
			p3[0] = Double.valueOf(if1.getNode().getLng()).doubleValue();
			p3[1] = Double.valueOf(if1.getNode().getLat()).doubleValue();
			p4[0] = Double.valueOf(if2.getNode().getLng()).doubleValue();
			p4[1] = Double.valueOf(if2.getNode().getLat()).doubleValue();
			
			prjp3[0]= projlng(p3[0]);
			prjp3[1]= projlat(p3[1]);
			prjp4[0]= projlng(p4[0]);
			prjp4[1]= projlat(p4[1]);		
			
			
			a[0] = prjp1[0]-cosine-sine;
			a[1] = prjp1[1]-sine+cosine;
			b[0] = prjp1[0]-cosine+sine;
			b[1] = prjp1[1]-sine-cosine;
			sect1 = intersect(a,b,prjp3,prjp4);
			
			a[0] = prjp1[0]-cosine-sine;
			a[1] = prjp1[1]-sine+cosine;
			b[0] = prjp2[0]+cosine-sine;
			b[1] = prjp2[1]+sine+cosine;				
			sect2 = intersect(a,b,prjp3,prjp4);
			
			a[0] = prjp2[0]+cosine-sine;
			a[1] = prjp2[1]+sine+cosine;
			b[0] = prjp2[0]+cosine+sine;
			b[1] = prjp2[1]+sine-cosine;
			sect3 = intersect(a,b,prjp3,prjp4);
			
			a[0] = prjp2[0]+cosine+sine;
			a[1] = prjp2[1]+sine-cosine;
			b[0] = prjp1[0]-cosine+sine;
			b[1] = prjp1[1]-sine-cosine;
			sect4 = intersect(a,b,prjp3,prjp4);	
			
			a[0] = prjp1[0]-cosine-sine;
			a[1] = prjp1[1]-sine+cosine;
			b[0] = prjp2[0]+cosine+sine;
			b[1] = prjp2[1]+sine-cosine;
			
			boolean f = (sect1=="YES") || (sect2=="YES") || (sect3=="YES") || (sect4=="YES") || 
			(rectLine(prjp3,prjp4,a,b)=="YES") || (distlatlng(p1,p3)<=1) || (distlatlng(p1,p4)<=1) || (distlatlng(p2,p3)<=1) || (distlatlng(p2,p4)<=1) || (rectPoint(prjp3,a,b)=="YES") || (rectPoint(prjp4,a,b)=="YES");
			if (f) {
				result.add(link.getKeyStr());
				//result.add(sect4);
			}
			
			
			
		} // for
		return result;
	}
	
	public static String intersect(double p1[], double p2[], double p3[], double p4[]) {

		
		double[] solution = new double[2];
		double[] a = new double[2];
		double[] b = new double[2];
		double[] c = new double[2];
	
		
		//a,b,c
		a[0]=p2[1]-p1[1];
		b[0]=p1[0]-p2[0];
		c[0]=-(p2[0]*p1[1]-p1[0]*p2[1]);
		a[1]=p4[1]-p3[1];
		b[1]=p3[0]-p4[0];
		c[1]=-(p4[0]*p3[1]-p3[0]*p4[1]);
		
		String result="";
		double d = a[0]*b[1]-a[1]*b[0];
		double dx = c[0]*b[1]-c[1]*b[0];
		double dy = a[0]*c[1]-a[1]*c[0];  
			
		boolean check = (dx==0) && (dy==0);
		
		if (d!=0) {
			solution[0]=dx/d;
			solution[1]=dy/d;
			result=accept(solution,p1,p2,p3,p4);
		}
		else if (d==0 && check) {
			result=rectLine(p4,p3,p2,p1);
		}
		else if (d==0 && !check) {
			result="NO";
		}
		return result;
			
	}
	
	public static String accept(double s[], double p1[], double p2[], double p3[], double p4[]) {
		
		String result="";
		boolean acceptx = ((s[0]>=Math.min(p1[0],p2[0]) && s[0]<=Math.max(p1[0],p2[0])) && (s[0]>=Math.min(p3[0],p4[0]) && s[0]<=Math.max(p3[0],p4[0])));
		boolean accepty = ((s[1]>=Math.min(p1[1],p2[1]) && s[1]<=Math.max(p1[1],p2[1])) && (s[1]>=Math.min(p3[1],p4[1]) && s[1]<=Math.max(p3[1],p4[1])));
		
		if (acceptx && accepty) {
			result="YES";
		}
		else {
			result="NO";
		}
		return result;
	}
	
	public static String rectLine(double point1[], double point2[], double a[], double b[]) {
		
		String result="";
		
		// Point1
		boolean x1 = (point1[0]<=Math.max(a[0],b[0])) && (point1[0]>=Math.min(a[0],b[0])); 
		boolean y1 = (point1[1]<=Math.max(a[1],b[1])) && (point1[1]>=Math.min(a[1],b[1]));
			
		
		//Point2
		boolean x2 = (point2[0]<=Math.max(a[0],b[0])) && (point2[0]>=Math.min(a[0],b[0])); 
		boolean y2 = (point2[1]<=Math.max(a[1],b[1])) && (point2[1]>=Math.min(a[1],b[1]));
		
		//Point1//Point2
		boolean x3 = (point1[0]==point2[0]) || (point1[1]==point2[1]);  
		boolean x4 = (point1[0]==a[0]);
		
		
		if ((x1 && y1) || (x2 && y2)) {
			result="YES";
		}
		else {
			result="NO";
		}
		return result;
	}
	
	public static String rectPoint(double point[], double a[], double b[]) {
		
		String result="";
		
		// Point1
		boolean x1 = (point[0]<=Math.max(a[0],b[0])) && (point[0]>=Math.min(a[0],b[0])); 
		boolean y1 = (point[1]<=Math.max(a[1],b[1])) && (point[1]>=Math.min(a[1],b[1]));
					
		
		if (x1 && y1) {
			result="YES";
		}
		else {
			result="NO";
		}
		return result;
	}
	
	public static double dist(double p1[], double p2[]) {
		
		double distance = Math.sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1]));
		return distance;
	}
	
	public static double projlat(double lat) {
		double r = 6378.137;
		lat = Math.toRadians(lat);
		return r*Math.log(Math.tan((Math.PI)/4 + lat/2));
	}
	
	public static double projlng(double lng) {
		double r = 6378.137;
		lng = Math.toRadians(lng);
		return r*lng;
	}
	
	public static double distlatlng(double a[], double b[]) {
		double r = 6378.137;
		double dlat = Math.toRadians(a[1]-b[1]);
		double dlon = Math.toRadians(a[0]-b[0]);
		double lat1 = Math.toRadians(a[1]);
		double lat2 = Math.toRadians(b[1]);
		double e = Math.sin(dlat/2)*Math.sin(dlat/2) + Math.sin(dlon/2)*Math.sin(dlon/2)*Math.cos(lat1)*Math.cos(lat2);
		double c = 2*Math.atan2(Math.sqrt(e),Math.sqrt(1-e));
		double d = r*c;
		return d;
	}	
}